#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

/**
一道面试题：
    编写程序完成如下功能：
        1）有一int型全局变量g_Flag初始值为0；
        2）在主线称中启动线程1，打印“this is thread1”，并将g_Flag设置为1
        3）在主线称中启动线程2，打印“this is thread2”，并将g_Flag设置为2
        4）线程序1需要在线程2退出后才能退出
        5）主线程在检测到g_Flag从1变为2，或者从2变为1的时候退出
**/

#define T_CHECK(x)      {int a = x; if (a != 0) {printf("Unexpected return: %d\n", a); return a;}}

static int g_flag = 0;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static void *thread1(void *arg)
{
    printf("Enter thread 1, g_flag: %d\n", g_flag);
    pthread_mutex_lock(&mutex);
    printf("Changing g_flag from %d to 1\n", g_flag);
    if (g_flag == 2) {
        printf("pthread1_cond_signal\n");
        pthread_cond_signal(&cond);
    }
    g_flag = 1;
    pthread_mutex_unlock(&mutex);
    pthread_join(*(pthread_t *)arg, NULL);

    printf("Leave thread 1, g_flag: %d\n", g_flag);

    return NULL;
}

static void *thread2(void *arg)
{
    printf("Enter thread 2, g_flag: %d\n", g_flag);
    pthread_mutex_lock(&mutex);
    printf("Changing g_flag from %d to 2\n", g_flag);
    if (g_flag == 1) {
        printf("pthread2_cond_signal\n");
        pthread_cond_signal(&cond);
    }
    g_flag = 2;
    pthread_mutex_unlock(&mutex);
    printf("Leave thread 2, g_flag: %d\n", g_flag);

    return NULL;
}

int main(int argc, char const *argv[])
{
    pthread_t tid1, tid2;

    setbuf(stdout, NULL);
    printf("Enter main, g_flag: %d\n", g_flag);

    T_CHECK(pthread_create(&tid2, NULL, thread2, NULL));
    T_CHECK(pthread_create(&tid1, NULL, thread1, (void *)&tid2));

    printf("Leave main thread, g_flag: %d\n", g_flag);

    pthread_cond_wait(&cond, &mutex);
/*  等待 g_flag 由 1 变 2 或者 2 变 1；而初始值为 0，
    所以必须要执行线程 1 到更新 g_flag 为 1 再执行线程 2 更新 g_flag 到 2，触发条件 1->2；
    或者先执行执行线程 2 更新 g_flag 到 2 再执行线程 1 到更新 g_flag 为 1，触发条件 2->1；
    由于线程 1 要在线程 2 后退出，所以有可能 1 未执行完，进程已经在触发条件下退出了*/

    // pthread_exit(&ret);  // 退出主线程，则后面的语句不再执行，且所有子线程执行完再退出；
                            // 否则进程退出，其他子线程不保证能执行完
                            // 也可以使用 pthread_join 等待其他线程执行完成

    printf("Leave main, g_flag: %d\n", g_flag);

    return 0;
}
